﻿using System;
using System.Web.Mvc;
using CashFlow.Entities;
using CashFlow.Entities.Models;
using CashFlow.Repository;
using CashFlow.Web.Controllers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Linq;
using Should.Fluent;
using System.Collections.Generic;

namespace CashFlow.Web.Tests.Controllers
{
	[TestClass]
	public class BillControllerTests : ControllerTestBase
	{
		private BillController controller;
		private Mock<IBillRepository> repositoryMock;
		private IBillRepository repository;

		private const string IndexViewName = "Index";
		private const string EditViewName = "Edit";

		[TestInitialize]
		public void IniTests()
		{
			repositoryMock = new Mock<IBillRepository>();
			repository = repositoryMock.Object;
			controller = new BillController(repository);
		}

		#region Get
		[TestMethod]
		public void Index_Get_With_Existing_2_Entities_Returns_Model()
		{
			var entities = new List<Bill>
			{
				new Bill{Id = "1", Details = "d"},
				new Bill{Id = "2", Details = "d2"},
			};
			repositoryMock.Setup(x => x.GetAll()).Returns(entities);

			var actual = controller.Index() as ViewResult;
			var model = actual.ViewData.Model as IEnumerable<BillListModel>;
			model.Should().Not.Be.Null();
			model.Count().Should().Equal(2);
		}

		[TestMethod]
		public void Index_Get_Without_Existing_Entities_Returns_Empty_Model()
		{
			var entities = new List<Bill>();
			repositoryMock.Setup(x => x.GetAll()).Returns(entities);

			var actual = controller.Index() as ViewResult;
			var model = actual.ViewData.Model as IEnumerable<BillListModel>;
			model.Should().Not.Be.Null();
			model.Count().Should().Equal(0);
		}
		#endregion

		#region Add
		[TestMethod]
		public void Add_Get_Returns_Default_Bill()
		{

			var actual = controller.Add() as ViewResult;
			var model = actual.ViewData.Model as BillModel;

			model.Should().Not.Be.Null();
			model.AccountId.Should().Equal(Account.GetDefault().Id);
		}

		[TestMethod]
		public void Add_Post_If_Succesful_Redirects_To_Bill_List()
		{
			repositoryMock.Setup(r => r.Create(It.IsAny<Bill>())).Verifiable();
			var model = new BillModel { PayeeName = "n" };

			var actual = controller.Add(model);

			ShouldRedirectToRouteResult(actual, IndexViewName);
		}

		[TestMethod]
		public void Add_Post_If_Model_Error_Returns_Back_To_Edit()
		{
			repositoryMock.Setup(r => r.Create(It.IsAny<Bill>())).Verifiable();
			var model = new BillModel();
			controller.ModelState.AddModelError("", CashFlow.Web.Properties.Resources.ErrorInvalidModel);

			var actual = controller.Add(model);

			actual.Should().Not.Be.Null();
			var actionResult = actual as ViewResult;
			actionResult.Should().Not.Be.Null();
			controller.ModelState[""].Errors[0].ErrorMessage.
				Should().Equal(CashFlow.Web.Properties.Resources.ErrorInvalidModel);
		}

		[TestMethod]
		public void Add_Post_If_Save_Failed_Redirects_To_Bill_List_And_Records_Error()
		{
			repositoryMock.Setup(r => r.Create(It.IsAny<Bill>())).Throws<Exception>();
			var model = new BillModel { PayeeName = "n" };

			var actual = controller.Add(model);

			ShouldRedirectToRouteResult(actual, IndexViewName);
			//TODO: vvs p2 test that error recorded for UI (after implementation of error indicator).
		}
		#endregion

		#region Edit
		[TestMethod]
		public void Edit_Get_If_Bill_Not_Found_Redirects_To_Index()
		{
			Bill nullBill = null;
			repositoryMock.Setup(r => r.GetById(It.IsAny<string>())).Returns(nullBill);

			var actual = controller.Edit("id does not exist");

			ShouldRedirectToRouteResult(actual, IndexViewName);
		}

		[TestMethod]
		public void Edit_Get_If_Bill_Found_Redirects_To_Edit_View()
		{
			var bill = new Bill(new Tenant("1")) { Recurrence = new RecurrenceOnce() };
			repositoryMock.Setup(r => r.GetById(It.IsAny<string>())).Returns(bill);

			var actual = controller.Edit("existingId");

			ShouldRedirectToView(actual, EditViewName);
			var model = ((ViewResult)actual).Model as BillModel;
			model.Should().Not.Be.Null();
			model.TenantId.Should().Equal("1");
		}
		#endregion

	}
}
